import 'dart:developer';
import 'package:flutter_scaffold_plugin/pages/vidu/remote_participant.dart';
import 'package:flutter_scaffold_plugin/pages/vidu/websocket_listener.dart';
import 'package:flutter_webrtc/flutter_webrtc.dart';
import 'package:web_socket_channel/io.dart';
import 'package:flutter_webrtc/flutter_webrtc.dart' as WebRTC;

class PeersManager {
  RTCPeerConnection localPeer;
  RTCPeerConnection remotePeer;
  WebSocketListener webSocketListener;
  IOWebSocketChannel webSocket;
  MediaStreamTrack localVideoTrack;
  MediaStreamTrack localAudioTrack;
  List<WebRTC.MediaStream> remoteStreams = [];
  RemoteParticipant remoteParticipant;
  String get sdpSemantics => 'unified-plan';

  setWebSocketAdapter(WebSocketListener webSocketAdapter) {
    this.webSocketListener = webSocketAdapter;
  }

  start() async {
    var configuration = <String, dynamic>{
      'iceServers': [
        {
          'url': 'stun:stun.l.google.com:19302',
        },
      ],
      'sdpSemantics': sdpSemantics,
    };

    final mediaConstraints = <String, dynamic>{
      'mandatory': {
        "OfferToReceiveAudio": "true",
        "OfferToReceiveVideo": "true"
      },
      'optional': [
        {
          'DtlsSrtpKeyAgreement': false,
        },
      ],
    };
    localPeer = await createPeerConnection(configuration, mediaConstraints);

    localPeer.onSignalingState = (state) {
      this.onSignalingState(state, true);
    };
    localPeer.onIceGatheringState = (state) {
      this.onIceGatheringState(state, true);
    };
    localPeer.onIceConnectionState = (state) {
      this.onIceConnectionState(state, true);
    };
    localPeer.onConnectionState = (state) {
      this.onPeerConnectionState(state, true);
    };
    localPeer.onIceCandidate = (candidate) {
      this.onCandidate(candidate, true);
    };
    localPeer.onRenegotiationNeeded = () {
      this.onRenegotiationNeeded(true);
    };

    localPeer.onAddStream = (stream) {
      this.onAddStream(stream, true);
    };
    localPeer.onRemoveStream = (stream) {
      this.onRemoveStream(stream, true);
    };
  }

  createLocalOffer(Map<String, dynamic> mediaConstraints) async {
    RTCSessionDescription sessionDescription =
        await localPeer.createOffer(mediaConstraints);
    try {
      this.localPeer.setLocalDescription(sessionDescription);
      Map<String, String> localOfferParams = {};
      localOfferParams["audioActive"] = "true";
      localOfferParams["videoActive"] = "true";
      localOfferParams["doLoopback"] = "false";
      localOfferParams["hasAudio"] = "true";
      localOfferParams["hasVideo"] = "true";
      localOfferParams["frameRate"] = "30";
      localOfferParams["typeOfVideo"] = "CAMERA";
      localOfferParams["videoDimensions"] = "{\"width\":1280,\"height\":720}";
      localOfferParams["sdpOffer"] = sessionDescription.sdp;
      if (this.webSocketListener.id > 1) {
        this.webSocketListener.sendJson("publishVideo", localOfferParams);
      } else {
        this.webSocketListener.localOfferParams = localOfferParams;
      }
    } catch (e) {
      print("Local Peer local Description set: " + e.toString());
    }
  }

  createRemotePeerConnection(RemoteParticipant remoteParticipant) async {
    var configuration = <String, dynamic>{
      'iceServers': [
        {
          'url': 'stun:stun.l.google.com:19302',
        },
      ],
      'sdpSemantics': sdpSemantics,
    };

    final constraints = <String, dynamic>{
      'mandatory': {
        "OfferToReceiveAudio": "true",
        "OfferToReceiveVideo": "true"
      },
      'optional': [
        {
          'DtlsSrtpKeyAgreement': false,
        },
      ],
    };
    this.remotePeer = await createPeerConnection(configuration, constraints);
    remoteParticipant.peerConnection = this.remotePeer;
    this.remoteParticipant = remoteParticipant;
    this.remoteParticipant?.peerConnection = this.remotePeer;
  }

  /// peerConnection callback
  void onSignalingState(RTCSignalingState state, bool isLocal) {
    log(state.toString() + ":onSignalingState:" + isLocal.toString());
  }

  void onIceGatheringState(RTCIceGatheringState state, bool isLocal) {
    log(state.toString() + ":onIceGatheringState:" + isLocal.toString());
  }

  void onIceConnectionState(RTCIceConnectionState state, bool isLocal) {
    log(state.toString() + ":onIceConnectionState:" + isLocal.toString());
  }

  void onPeerConnectionState(RTCPeerConnectionState state, bool isLocal) {
    log(state.toString() + ":onPeerConnectionState:" + isLocal.toString());
  }

  void onAddStream(MediaStream stream, bool isLocal) {
    if (isLocal) {
      log("local peerConnection did add stream");
    } else {
      log("remote peerConnection did add stream");
      if (stream.getAudioTracks().length > 1 ||
          stream.getVideoTracks().length > 1) {
        log("Weird looking stream");
      }
      remoteStreams.add(stream);
    }
  }

  void onRemoveStream(MediaStream stream, bool isLocal) {
    log("did remove stream");
  }

  void onCandidate(RTCIceCandidate candidate, bool isLocal) {
    print('onCandidate: ${candidate.candidate}');

    if (isLocal) {
      Map<String, String> iceCandidateParams = {};
      iceCandidateParams["sdpMid"] = candidate.sdpMid;
      iceCandidateParams["sdpMLineIndex"] = candidate.sdpMlineIndex.toString();
      iceCandidateParams["candidate"] = candidate.candidate;
      if (this.webSocketListener.userId != null) {
        iceCandidateParams["endpointName"] = this.webSocketListener.userId;
        this.webSocketListener.sendJson("onIceCandidate", iceCandidateParams);
      } else {
        this.webSocketListener.addIceCandidate(iceCandidateParams);
      }
      log("NEW local ice candidate");
    } else {
      Map<String, String> iceCandidateParams = {};
      iceCandidateParams["sdpMid"] = candidate.sdpMid;
      iceCandidateParams["sdpMLineIndex"] = candidate.sdpMlineIndex.toString();
      iceCandidateParams["candidate"] = candidate.candidate;
      iceCandidateParams["endpointName"] = this.remoteParticipant.id;
      this.webSocketListener.sendJson("onIceCandidate", iceCandidateParams);
      log("NEW remote ice candidate");
    }
  }

  void onRenegotiationNeeded(bool isLocal) {
    if (isLocal) {
      log("local peerConnection should negotiate");
    } else {
      log("remote peerConnection should negotiate");
    }
  }
}
